using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.FxCop.Sdk;
using Microsoft.BizTalk.XLANGs.BTXEngine;
using System.Reflection;
using System.IO;
using Microsoft.BizTalk.ExplorerOM;
using System.Collections;
using System.Xml;

namespace BizTalkCop.Rules.Bases
{
    /// <summary>
    /// Base class for rules running over configured BizTalk applications
    /// </summary>
    /// <remarks>
    /// The BizTalk application to run over is specified in the settings for the ApplicationConfig rule;
    /// rules are triggered by the first module in the FxCop project, but run only once for each Application
    /// </remarks>
    public abstract class ApplicationRuleBase : ConfiguredRuleBase
    {
        #region Private fields

        private bool _executed = false;
        private static Dictionary<string, Application> _applications;
        private static string _serverName;
        private static string _databaseName;
        private static string _applicationList;

        #endregion

        #region Private properties

        private static Dictionary<string, Application> Applications
        {
            get
            {
                LoadApplications();
                return _applications;
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Constructor with initial state
        /// </summary>
        /// <param name="name">Rule name, key for Rules.xml</param>
        protected ApplicationRuleBase(string name) : base(name) { }

        static ApplicationRuleBase()
        {
            LoadApplications();
        }

        #endregion

        #region Instance methods

        /// <summary>
        /// Uses the module check to initiate Application rules
        /// </summary>
        /// <param name="type">Module to check (not used)</param>
        /// <returns>Problem collection</returns>
        public override ProblemCollection Check(ModuleNode type)
        {
            //as we hook into the module check, makle sure the rule only executes
            //once per run - not once for each module in the project:
            if (!_executed)
            {
                foreach (Application application in Applications.Values)
                {
                    Check(application);
                }
                _executed = true;
            }
            return Problems;
        }

        /// <summary>
        /// Checks the application configuration cofems to the rule
        /// </summary>
        /// <param name="application">Application to check</param>
        /// <returns>Problem collection</returns>
        protected virtual ProblemCollection Check(Application application)
        {
            return Problems;
        }

        #endregion

        #region Private static methods

        /// <summary>
        /// Loads and caches ExplorerOM applications for the configured apps
        /// </summary>
        private static void LoadApplications()
        {            
            ApplicationConfig config = new ApplicationConfig();
            //check to see if the cache needs reloading:
            if (config.ApplicationName != _applicationList || config.BizTalkServer != _serverName || config.DatabaseName != _databaseName)
            {
                //only add applications if they have been specified in settings:
                _applications = new Dictionary<string, Application>();
                if (config.ApplicationName != ApplicationConfig.Default.ApplicationName)
                {
                    List<string> applicationNames = new List<string>(config.ApplicationName.Split(';'));
                    try
                    {
                        BtsCatalogExplorer explorer = new BtsCatalogExplorer();
                        explorer.ConnectionString = string.Format("Server={0};Database={1};Integrated Security=SSPI", config.BizTalkServer, config.DatabaseName);
                        foreach (Application application in explorer.Applications)
                        {
                            if (applicationNames.Contains(application.Name))
                            {
                                _applications.Add(application.Name, application);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(string.Format("Unable to load BizTalk applications: {0}", ex.Message), ex);
                    }
                }
                _applicationList = config.ApplicationName;
                _serverName = config.BizTalkServer;
                _databaseName = config.DatabaseName;
            }
        }

        #endregion
    }
}
